/*
 *
 *
 *               Create the Game management Objects for Daifugo game
 *
 *
 * */


var utils = require("../../../common/util/utils"),
    log4js = require('log4js'),
    logger = log4js.getLogger('ConnectHandler'),
    aiutils = require('./aiutils'),
    setUtils = require('./setUtils.js');


/***********************************
 *   Card object to play a game
 *
 *   1 : Ace
 *   2 <= i <= 10 : card rank i-th
 *   11: Jack
 *   12: Queen
 *   13: King
 *   14: Joker
 ************************************/

// Enum for card color


/** @constructor
 *
 *  Card initiation
 *  @this {Card}
 * @param {Number} rank
 * @param {Card.Color} color
 *
 */
function Card(rank, color) {
    this.mRank = rank;
    this.mColor = color;
    this.index = null;
}

/** @enum
 *
 *  Enum describing card color:
 *  - 4 normal colors
 *  - 1 joker color
 *
 */
Card.Color = {
    c: 0, // Clubs
    s: 1, // Spades
    d: 2, // Diamonds
    h: 3, // Hearts
    j: 4  // Rank
}


// Number of Joker card
Card_Joker = aiutils.Card_Joker;

/*
 *
 *   Compare function
 *   @function
 *   @param {Card}cx
 *   @param {Card}cy
 *   return -1 if cx < cy; 0 if cx == cy; 1 if cx > cy
 **/
var CardcmpPositive = function (cx, cy) {
    return cx.mRank * 5 - cy.mRank * 5 + cx.mColor - cy.mColor;
}


//************************************
/** @constructor
 *
 *  Create instance of PlayerGame
 * @this {PlayerGame}
 * @param {GameCards} game
 * @param {PlayerGame.Type} type
 * @param {PlayerGame.Level} level
 *
 */
function PlayerGame(game, type) {
    this.mGame = game;
    this.mType = type;
    this.mCards = [];
    this.nextPlayer = null;
    this.prevPlayer = null;
    this.status = PlayerStatus.Normal;
    this.mIndex = 0;
    this.lenCards = 0;

    this.setGame = function (game) {
        this.mGame = game;
    }

    this.setType = function (type) {
        this.mType = type;
    }

    this.setLevel = function (level) {
        this.mLevel = level;
    }

    this.addCard = function (card) {
        card.deal = false;
        this.mCards.push(card);
        this.lenCards++;
    }
    this.removeCard = function (card) {
        for (var j=0; j< card.length; j++){
            for (var i = 0; i < this.mCards.length; i++) {
                if (card[j].index == this.mCards[i].index) {
                    this.mCards[card[j].index].deal = true;
                }
            }
        }
        var ret = 0;
        for (var i = 0; i < this.mCards.length; i++) {
            if (this.mCards[i].deal == false) {
                ret++;
            }
        }
        this.lenCards = ret;
        if (ret == 0) {
            return true;
        }
        else {
            return false;
        }
    }


}

/** @enum
 *
 *  Enum describing Player type:
 *  - CPU
 *  - Human
 *
 */
PlayerType = {
    CPU: 0,
    Human: 1
}

/** @enum
 *
 *  Enum describing Player level:
 *  - Easy
 *  - Normal
 *  - Hard
 */
PlayerLevel = {
    Easy: 0,
    Normal: 1,
    Hard: 2
}

/**
 *      @enum
 *      Player Status
 */
PlayerStatus = {
    Normal: 0,
    Daifugo: 1,
    Fugo: 2,
    Hinmin: 3,
    Daihinmin: 4
};

//************************************

/** @constructor
 *
 *  Create an instance of TourGame
 *  @this {TourGame}
 *  @param {GameCards} game
 *  @param {PlayerGame} firstPlayer
 */
function TourGame(game, firstPlayer) {
    this.mDCards = [];
    // TODO: Using the member mColorLock for checking the lockinghand rule on the case it is activated
    this.mColorLock = [];
    this.isEightFlush = false;
    this.cPlayer = firstPlayer;
    this.tableDealer = [];
    this.cardsLock = [];
    // Create the table dealer
    for (var i = 0; i < game.mPlayers.length; i++) {
        if (game.mPlayers[i].status == PlayerStatus.Normal) {
            this.tableDealer[i] = true;
        }
        else {
            this.tableDealer[i] = false;
        }
    }

    // Player deal a cards set
    this.playerDealCard = function (icards) {
        var dcards = [];
        // TODO: check by rules
        var joker = null;
        for (var i = 0; i < icards.length; i++) {
            var card = this.cPlayer.mCards[icards[i].index];
            if (card.mRank != Card_Joker) {
                game.mCardsTable[card.mRank][card.mColor] = false;
            }
            else {
                game.mCardsTable[Card_Joker] = false;
                joker = card;
            }
            dcards.push(card);
        }

        // Check and inform the locking hand

        if (icards.length > 0) {
            this.cardsLock = icards;
        }


        // TODO: reuse-code at here
        var isFinish = this.cPlayer.removeCard(dcards);

        this.mDCards.push(new CardsDeck(this.cPlayer, dcards));

        this.tableDealer[this.cPlayer.mIndex] = true;
        // IsFinish
        if (isFinish) {
            this.cPlayer.status = game.endingRank;
            this.cPlayer.prevPlayer.nextPlayer = this.cPlayer.nextPlayer;
            this.cPlayer.nextPlayer.prevPlayer = this.cPlayer.prevPlayer;
        }
        // Change the player
        this.cPlayer = this.cPlayer.nextPlayer;
        this.tableDealer[this.cPlayer.mIndex] = false;
        return isFinish;
    }

    // Change the status when the player pass card
    this.playerPassCard = function () {
        this.mDCards.push(new CardsDeck(this.cPlayer, []));
        this.tableDealer[this.cPlayer.mIndex] = false;
        this.cPlayer = this.cPlayer.nextPlayer;
        this.tableDealer[this.cPlayer.mIndex] = false;
    }

    // Check the ending game
    this.checkEndingTour = function () {
        for (var i = 0; i < this.tableDealer.length; i++) {
            if (this.tableDealer[i] == true &&
                game.mPlayers[i].status == PlayerStatus.Normal) {
                return false;
            }
        }
        return true;
    }

    // Get Solution
}

/** @constructor
 *
 *  Create an instance of CardsDeck
 *  @this {TourGame}
 *  @param {arr of Card} cards
 *  @param {PlayerGame} player
 */
function CardsDeck(player, cards) {
    this.player = player;
    this.cards = cards;
}
//***********************************

//************************************
/** @constructor
 *
 *  GameCards initiation
 *  @this {GameCards}
 *
 */
GameCards = function () {

    this.mPlayers = [];

    this.mTourGame = [];

    this.mCardsTable = [];

    this.currentTourGame = null;

    this.endingRank = PlayerStatus.Normal;

    this.addPlayer = function (type) {
        var player = new PlayerGame(this, type);
        if (this.mPlayers.length > 0) {
            this.mPlayers[this.mPlayers.length - 1].nextPlayer = player;
            player.prevPlayer = this.mPlayers[this.mPlayers.length - 1];
            player.nextPlayer = this.mPlayers[0];
            this.mPlayers[0].prevPlayer = player;
        }
        player.mIndex = this.mPlayers.length;
        this.mPlayers.push(player);
    }

    // Initiation the game
    this.init = function () {
        var that = this;
        // Init the cards Table
        for (var i = 3; i < Card_Joker; i++) {
            this.mCardsTable[i] = [];
            for (var j = 0; j < Card.Color.j; j++) {
                this.mCardsTable[i][j] = true;
            }
        }
        this.mCardsTable[Card_Joker] = true;

        //Init the Cards
        var cards = [];

        // Init the normal card
        for (var i = 3; i < Card_Joker; i++) {
            for (var j = 0; j < Card.Color.j; j++) {
                cards.push(new Card(i, j));
            }
        }

        // Init the Joker card: Total 52 items cards
        //cards.push(new Card(Card_Joker, Card.Color.j));

        // Shuffle random the cards
        cards = utils.shuffle(cards);

        // Adding card for the players
        for (var i = 0; i < cards.length; i++) {
            this.mPlayers[i % this.mPlayers.length].addCard(cards[i]);
        }

        /*
         *
         * Test Case
         *
         * */

        //var mCards1 = [];
        //mCards1.push(new Card(4,2));
        //mCards1.push(new Card(6,0));
        //mCards1.push(new Card(7,0));
        //mCards1.push(new Card(8,0));
        //mCards1.push(new Card(9,0));
        //mCards1.push(new Card(9,2));
        //mCards1.push(new Card(9,3));
        //mCards1.push(new Card(10,0));
        //mCards1.push(new Card(10,1));
        //mCards1.push(new Card(12,3));
        //mCards1.push(new Card(13, 1));
        //mCards1.push(new Card(13,0));
        //mCards1.push(new Card(14,0));
        //mCards1.push(new Card(14,2));
        //mCards1.push(new Card(14,3));
        //
        //var mCards2 = [];
        //
        //mCards2.push(new Card(3,0));
        //mCards2.push(new Card(3,2));
        //mCards2.push(new Card(4,3));
        //mCards2.push(new Card(5,0));
        //mCards2.push(new Card(6,1));
        //mCards2.push(new Card(7,1));
        //mCards2.push(new Card(7,3));
        //mCards2.push(new Card(11,0));
        //mCards2.push(new Card(11,2));
        //mCards2.push(new Card(12,0));
        //mCards2.push(new Card(12,1));
        //mCards2.push(new Card(13,1));
        //mCards2.push(new Card(15, 2));
        //
        //var mCards3 = [];
        //mCards3.push(new Card(3,1));
        //mCards3.push(new Card(3,3));
        //mCards3.push(new Card(4,1));
        //mCards3.push(new Card(6,2));
        //mCards3.push(new Card(6,3));
        //mCards3.push(new Card(7,2));
        //mCards3.push(new Card(9,1));
        //mCards3.push(new Card(10,2));
        //mCards3.push(new Card(11,1));
        //mCards3.push(new Card(15,0));
        //mCards3.push(new Card(15,1));
        //mCards3.push(new Card(15,3 ));
        //mCards3.push(new Card(16, 4));
        //
        //var mCards4 = [];
        //
        //mCards4.push(new Card(4,0));
        //mCards4.push(new Card(5,1));
        //mCards4.push(new Card(5,2));
        //mCards4.push(new Card(5, 3));
        //mCards4.push(new Card(8,1));
        //mCards4.push(new Card(8, 2));
        //mCards4.push(new Card(8,3));
        //mCards4.push(new Card(10,3));
        //mCards4.push(new Card(11,3));
        //mCards4.push(new Card(12,2));
        //mCards4.push(new Card(13,2));
        //mCards4.push(new Card(13,3));
        //mCards4.push(new Card(14,1));
        //
        //
        //this.mPlayers[3].mCards = mCards1;
        //this.mPlayers[2].mCards = mCards2;
        //this.mPlayers[1].mCards = mCards3;
        //this.mPlayers[0].mCards = mCards4;
        //

        /*
         *
         *
         * */


        //// Build shuffle to the players
        var playerCars = [];
        for (var i = 0; i < this.mPlayers.length; i++) {
            playerCars.push(this.mPlayers[i].mCards);
        }

        // Shuffle for player
        playerCars = utils.shuffle(playerCars);

        //// Sort all cards by rank
        for (var i = 0; i < this.mPlayers.length; i++) {
            this.mPlayers[i].mCards = playerCars[i];
            this.mPlayers[i].mCards.sort(
                function (a, b) {
                    return CardcmpPositive(a, b)
                });
            for (var j = 0; j < this.mPlayers[i].mCards.length; j++) {
                this.mPlayers[i].mCards[j].index = j;
            }
        }

        for (var i = 0; i < this.mPlayers.length; i++) {
            for (var j = 0; j < this.mPlayers[i].mCards.length; j++) {
                this.mPlayers[i].mCards[j].deal = false;
            }
        }
        this.checkFirstPlayer = 0;
        for (var i = 0; i < this.mPlayers.length; i++) {
            if(this.mPlayers[i].mCards[0].mRank == 3 && this.mPlayers[i].mCards[0].mColor == 0){
                this.checkFirstPlayer += i;
            }
        }




        // Init the first tournament
        this.currentTourGame = new TourGame(this, this.mPlayers[this.checkFirstPlayer]);
        //this.currentTourGame = new TourGame(this, this.mPlayers[3]);
        this.mTourGame.push(this.currentTourGame);


    };

    this.getMyCards = function (iplayer) {
        var ret = {myCards: null, ortherCards: [], positions: [], me: iplayer};
        for (var i = iplayer; i < iplayer + this.mPlayers.length; i++) {
            if (i == iplayer) {
                ret.myCards = this.mPlayers[i].mCards;
            }
            else {
                ret.ortherCards.push(this.mPlayers[i % this.mPlayers.length].mCards.length);
                ret.positions.push(i % this.mPlayers.length)
            }
        }
        ret.me = iplayer;
        return ret;
    }

    this.getSolutions = function (iplayer) {
        return this.mPlayers[iplayer].cutSolution();
    }

    this.getCurrentPlayer = function () {
        return this.currentTourGame.cPlayer.mIndex;
    }

    this.getDealingSolution = function (iPlayer) {
        return this.mPlayers[iPlayer].cutSolution();
    }

    // Print the players cards on console
    this.printCards = function () {
        for (var i = 0; i < this.mPlayers.length; i++) {
            var s = "Cards of Player " + i + ": ";
            for (var j = 0; j < this.mPlayers[i].mCards.length; j++) {
                s += this.mPlayers[i].mCards[j].mRank + "-" + this.mPlayers[i].mCards[j].mColor + " ";
            }
            logger.info(s);
            logger.info(this.mPlayers[i].mCards.length);
        }
    }

    // Decide the dealing cards
    // []: Pass card
    // [1, 3, 5]: Deal card indexes
    this.playerDecideCards = function (icards) {
        var ret = {
            player: this.currentTourGame.cPlayer.mIndex,
            isPlayerFinish: false,
            status: PlayerStatus.Normal,
            gameFinish: false,
            nextPlayer: null,
            isTourFinish: false,
            isEightFlush: false,
            isSpeedMax: false
        };
        var currentPlayer = this.currentTourGame.cPlayer;
        if (this.mPlayers[3].status != PlayerStatus.Normal) {
            ret.isSpeedMax = true;
        }
        if (icards.length > 0) {
            var isFinish = this.currentTourGame.playerDealCard(icards);
            ret.isFinish = isFinish;
            if (isFinish) {
                this.endingRank++;
                this.mPlayers[ret.player].status = this.endingRank;
                ret.status = this.endingRank;
            }
            if (ret.status == PlayerStatus.Hinmin) {
                this.endingRank++;
                this.currentTourGame.cPlayer.status = PlayerStatus.Daihinmin;
                ret.gameFinish = true;
            }

            //------------//
            /*
             *
             * 8 Flush
             *
             * */
            //ret.isEightFlush = aiutils.isEightFlush(icards);
            //if (ret.isEightFlush == true) {
            //    logger.info("Ending tour 8 Flush");
            //    var newTour = new TourGame(this, currentPlayer);
            //    this.currentTourGame = newTour;
            //    this.mTourGame.push(newTour);
            //    ret.isTourFinish = true;
            //}
        }
        else {

            // Checking the ending tournament
            this.currentTourGame.playerPassCard();
            if (this.currentTourGame.checkEndingTour()) {
                logger.info("Ending tour");
                var newTour = new TourGame(this, this.currentTourGame.cPlayer);
                this.currentTourGame = newTour;
                this.mTourGame.push(newTour);
                ret.isTourFinish = true;
            }
        }

        ret.nextPlayer = this.currentTourGame.cPlayer.mIndex;
        logger.info("Current tour status");
        logger.info(ret);
        return ret;
    }
    // Check the valid turn
    this.verifyTurn = function (iplayer) {
        return iplayer == this.currentTourGame.cPlayer.mIndex;
    }
}

module.exports = {
    GameCard: GameCards,
    'PlayerType': PlayerType,
    'PlayerLevel': PlayerLevel,
    'Joker': Card.Joker,
    PlayerStatus: PlayerStatus
};